#include "unit_tool.h"

#include <QDebug>
#include <QDataStream>

#include <cstdint>

/**
 转义数据
*/
QByteArray transformData(const QByteArray &data)
{
    QByteArray result;
    for (int i = 0; i < data.size(); i++) {
        if (data[i] == SignFlag) {
            result.push_back(0x7d);
            result.push_back(0x02);
        } else if (data[i] == 0x7d) {
            result.push_back(0x7d);
            result.push_back(0x01);
        } else {
            result.push_back(data[i]);
        }
    }
    return result;
}

/**
 反转义数据
*/
QByteArray turnTransformData(const QByteArray &data)
{
    QByteArray result;
    for (int i = 0; i < data.size(); i++) {
        if (data[i] == 0x7d) {
            result.push_back(data[i] + data[i + 1] - 0x01);
            i++;
        } else {
            result.push_back(data[i]);
        }
    }
    return result;
}

/**
生成校验码
*/
uint8_t generateCheckCode(const QByteArray &data)
{
    if (data.isEmpty())
        return 0;
    // 校验码指从消息头开始，同后一字节异或，直到校验码前一个字节，占用一个字节。
    uint8_t code = data[0];
    for (int i = 1; i < data.size(); i++) {
        code ^= data[i];
    }

    return code;
}

/**
 * @brief 校验数据校验码
 */
bool checkCode(const QByteArray &data)
{
    if (data.isEmpty())
        return false;

    QByteArray msg = data.mid(0, data.size() - 1);
    uint8_t code = generateCheckCode(msg);
    return code == (uint8_t)data[data.size() - 1];
}

Msg getMsg(const QByteArray &data)
{
    if (data.size() < 2) {
        return Msg();
    }
    // 判断标识符
    if (data.at(0) != SignFlag || data.at(data.size() - 1) != SignFlag) {
        return Msg();
    }

    // 去标识符
    QByteArray tmp = data.mid(1, data.size() - 2);
    // 转义还原
    QByteArray result = turnTransformData(tmp);
    // 验证校验码
    if (!checkCode(result)) {
        return Msg();
    }
    // 去校验码
    result = result.mid(0, result.size() - 1);
    // 解析消息
    QDataStream stream(&result, QIODevice::ReadOnly);

    Msg msg;
    stream >> msg;

    return msg;
}

QByteArray makeMsg(const Msg &msg)
{
    QByteArray data;
    // 封装消息
    QDataStream stream(&data, QIODevice::WriteOnly);
    stream << msg;

    // 计算并填充校验码
    data.push_back(generateCheckCode(data));
    // 转义
    data = transformData(data);

    QByteArray result;
    // 插入标识符
    result.push_back(SignFlag);
    // 插入消息
    result.push_back(data);
    // 插入标识符
    result.push_back(SignFlag);
    return result;
}
